async function fetchAndDecodeSubtree(url) {
    const cleanUrl = url.split('?')[0]; // 删除URL参数
    const extension = cleanUrl.split('.').pop(); // 获取文件扩展名

    const response = await fetch(url);
    if (!response.ok) {
        throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
    }

    if (extension === "json") {
        //  .json file
        const json = await response.json();
        return decodeSubtreeJSON(json, getDirectoryUrl(url));
    }
    if (extension === "subtree") {
        //  binary .subtree file
        const buffer = await response.arrayBuffer();
        return decodeSubtreeBinary(buffer, getDirectoryUrl(url));
    } else {
        throw new Error(`不支持 file extension: ${extension}`);
    }
}
function getDirectoryUrl(url) {
    const cleanUrl = url.split('?')[0]; // 删除URL参数
    return cleanUrl.substring(0, cleanUrl.lastIndexOf('/') + 1);
};

async function decodeSubtreeJSON(json, baseURL) {
    if (!json.buffers) {
        throw new Error(`subtree has no buffers`);
    }

    const bufferFetchPromises = json.buffers.map(async (buffer) => {
        if (!buffer.uri) {
            throw new Error(`bad subtree definition. subtrees in json format do not contain internal buffers`);
        }
        const binaryUrl = new URL(buffer.uri, baseURL).href; // Fixed to buffer.uri instead of externalBuffer.uri
        const response = await fetch(binaryUrl);
        if (!response.ok) {
            throw new Error(`Failed to fetch binary file ${binaryUrl}: ${response.statusText}`);
        }
        return response.arrayBuffer();
    });

    const buffers = await Promise.all(bufferFetchPromises);

    return { json: json, buffers: buffers };
}
// Decode binary .subtree file
async function decodeSubtreeBinary(buffer, baseURL) {
    const dataView = new DataView(buffer);
    const jsonByteLength = dataView.getBigUint64(8, true); // JSON byte length
    const binaryByteLength = dataView.getBigUint64(16, true); // Binary byte length

    const jsonStart = 24;
    const jsonEnd = jsonStart + Number(jsonByteLength);
    const binaryStart = jsonEnd + (8 - (jsonEnd % 8)) % 8; // Align to 8-byte boundary
    const binaryEnd = binaryStart + Number(binaryByteLength);

    const jsonBuffer = buffer.slice(jsonStart, jsonEnd);
    const jsonString = new TextDecoder().decode(jsonBuffer).trim();
    const json = JSON.parse(jsonString);
    if (!json.buffers) {
        throw new Error(`subtree has no buffers`);
    }

    const bufferFetchPromises = json.buffers.map(async (b) => {
        if (b.uri) {
            const binaryUrl = new URL(b.uri, baseURL).href;
            const response = await fetch(binaryUrl);
            if (!response.ok) {
                throw new Error(`Failed to fetch binary file ${binaryUrl}: ${response.statusText}`);
            }
            return new Uint8Array(await response.arrayBuffer());
        } else {
            // 在非uri情况下直接使用buffer,
            //它已经在函数作用域中可用。
            return new Uint8Array(buffer.slice(binaryStart, binaryEnd));
        }
    });
    const buffers = await Promise.all(bufferFetchPromises);
    for (let index = 0; index < json.bufferViews.length; index++) {
        const element = json.bufferViews[index];
        const { buffer, byteLength, byteOffset } = element
        const currBuffer = buffers[buffer]
        element.data = currBuffer.slice(byteOffset, byteOffset + byteLength)
    }
    if (json.childSubtreeAvailability) {
        if (json.childSubtreeAvailability.bitstream != null || json.childSubtreeAvailability.bitstream != undefined) {
            let bufferViewIndex = json.childSubtreeAvailability.bitstream
            json.childSubtreeAvailability.data = json.bufferViews[bufferViewIndex].data
        }
    }
    if (json.contentAvailability) {
        for (let index = 0; index < json.contentAvailability.length; index++) {
            const element = json.contentAvailability[index];
            if (element.bitstream != null || element.bitstream != undefined) {
                let bufferViewIndex =element.bitstream
                json.contentAvailability[index].data = json.bufferViews[bufferViewIndex].data
            }
        }
    }
    if (json.tileAvailability) {
        if (json.tileAvailability.bitstream != null || json.tileAvailability.bitstream != undefined) {
            let bufferViewIndex = json.tileAvailability.bitstream
            json.tileAvailability.data = json.bufferViews[bufferViewIndex].data
        }
    }
    return json;
}

export function isAvailable(availability, childBitIndex) {
    if (!availability) {
        throw new Error(`incomplete json subtree`);
    }
    if (availability.constant != null) {
        if (availability.constant == 0) {
            return false;
        }
        if (availability.constant == 1) {
            return true;
        }
    }
    if (availability.bitstream == undefined) {
        return
    }
    if (!availability.data) {
        return
    }

    return isAvailable2(availability.data, childBitIndex);
}

export function isAvailable2(buffer, index) {
    const byteIndex = index >> 3;
    const bitIndex = index % 8;

    return ((buffer[byteIndex] >> bitIndex) & 1) === 1;
}

export function isTileAvailable(address, subtreeData) {
    let childBitIndex = 0;
    if (address.z == undefined) {
        childBitIndex = computeQuadtreeIndex(address.x, address.y, address.level);
    } else {
        childBitIndex = computeOctreeIndex(address.x, address.y, address.z, address.level);
    }
    return isAvailable(subtreeData.tileAvailability, childBitIndex);
}
export function isContentAvailable(address, subtreeData, contentIndex) {
    let childBitIndex = 0;
    if (address.z == undefined) {
        childBitIndex = computeQuadtreeIndex(address.x, address.y, address.level);
    } else {
        childBitIndex = computeOctreeIndex(address.x, address.y, address.z, address.level);
    }
    const contentAvailability = subtreeData.contentAvailability[contentIndex]
    const flag = isAvailable(contentAvailability, childBitIndex)
    return flag;
}
export function isChildSubtreeAvailable(address, subtreeData) {
    let index = 0;
    if (address.z == undefined) {
        index = computeQuadtreeMotonIndex(address.x, address.y);
    } else {
        index = computeOctreeMotonIndex(address.x, address.y, address.z);
    }
    return isAvailable(subtreeData.childSubtreeAvailability, index, subtreeData);
}
// 处理JSON子树，获取外部二进制文件


function part1By1(n) {
    n &= 0x0000ffff;          //0000000000001111111111111111       
    n = (n ^ (n << 8)) & 0x00ff00ff;  //0000111111110000000011111111
    n = (n ^ (n << 4)) & 0x0f0f0f0f; // 001111000011110000111100001111
    n = (n ^ (n << 2)) & 0x33333333;  //110011001100110011001100110011
    n = (n ^ (n << 1)) & 0x55555555;  //1010101010101010101010101010101
    return n;
}

function computeQuadtreeIndex(x, y, level) {
    let offset = 0;
    if (level != null || level != undefined) {
        offset = (Math.pow(4, level) - 1) / 3;
    }
    return offset + (part1By1(x) | (part1By1(y) << 1));
}
function computeQuadtreeMotonIndex(x, y) {
    return (part1By1(x) | (part1By1(y) << 1));
}

function part1By2(n) {
    n &= 0x000003ff;                  // n = ------------------9876543210
    n = (n ^ (n << 16)) & 0xff0000ff; // n = ----9876--------5432--------10--------------------
    n = (n ^ (n << 8)) & 0x0300f00f;  // n = ----9--8--7--6--------5--4--3--2--------------------
    n = (n ^ (n << 4)) & 0x030c30c3;  // n = ----9-----8--7-----6--------5-----4--3-----2--------
    n = (n ^ (n << 2)) & 0x09249249;  // n = ----9--------8--------7--------6--------5--------4--------3--------2--------
    return n;
}

function computeOctreeIndex(x, y, z, level) {
    let offset = 0;
    if (level) {
        offset = (Math.pow(8, level) - 1) / 7;
    }
    return offset + (part1By2(x) | (part1By2(y) << 1) | (part1By2(z) << 2));
}
function computeOctreeMotonIndex(x, y, z) {
    return (part1By2(x) | (part1By2(y) << 1) | (part1By2(z) << 2));
}
function getAvailabilityResult(availabilityData, index) {
    if ('constant' in availabilityData) {
        return Boolean(availabilityData.constant);
    }

    if (availabilityData.bitstream != null) {
        return getBooleanValueFromBitstream(index, availabilityData.data);
    }
    return false;
}
function getBooleanValueFromBitstream(
    availabilityIndex,
    availabilityBuffer
) {
    const byteIndex = Math.floor(availabilityIndex / 8);
    const bitIndex = availabilityIndex % 8;
    const bitValue = (availabilityBuffer[byteIndex] >> bitIndex) & 1;

    return bitValue === 1;
}

export {
    fetchAndDecodeSubtree,
    getAvailabilityResult,
};